package com.shakespace.template.extensions

import android.app.Activity
import android.app.Dialog
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import androidx.annotation.MainThread
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import androidx.viewbinding.ViewBinding
import java.lang.reflect.ParameterizedType
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty


inline fun <reified V : ViewBinding> viewBindingV1() = viewBindingV1(V::class.java)

// 使用反射
inline fun <reified T : ViewBinding> viewBindingV1(clazz: Class<T>): FragmentViewBindingPropertyV1<Fragment, T> {
    val bindMethod = clazz.getMethod("bind", View::class.java)
    return FragmentViewBindingPropertyV1 {
        bindMethod(null, it.requireView()) as T
    }
}

/**
 * 不是用反射
 * params1 : 一个函数，传入View 返回ViewBinding
 * params2 ： 一个函数，传入Fragment,返回View
 * requireView，本身并不接收参数，但是实际需要一个具体的fragment来调用
 * 使用 Fragment::requireView 时，它就是一个函数对象，接收一个Fragment,返回View
 * 比较：
 * fun getViews(fragment: Fragment): View = fragment.requireView()
 * fun getViews2(fragment: Fragment): (Fragment) -> View = ::getViews
 */
inline fun <F : Fragment, V : ViewBinding> viewBindings(
        crossinline viewBinder: (View) -> V,
        crossinline viewProvider: (F) -> View = Fragment::requireView
) = FragmentViewBindingPropertyV1 { fragment: F ->
    viewBinder(viewProvider(fragment))
}

inline fun <F : Fragment, V : ViewBinding> viewBindings2(
        crossinline viewBinder: (View) -> V,
        crossinline viewProvider: (F) -> View = Fragment::requireView
): ReadOnlyProperty<F, V> {
    return FragmentViewBindingPropertyV1 { fragment: F -> viewBinder(viewProvider(fragment)) }
}

/**
 * @param viewBinder 创建绑定类对象
 */
class FragmentViewBindingPropertyV1<in F : Fragment, out V : ViewBinding>(
        private val viewBinder: (F) -> V
) : ReadOnlyProperty<F, V> {

    private var viewBinding: V? = null

    @MainThread
    override fun getValue(thisRef: F, property: KProperty<*>): V {
        // 已经绑定，直接返回
        viewBinding?.let { return it }

        // Use viewLifecycleOwner.lifecycle other than lifecycle
        val lifecycle = thisRef.viewLifecycleOwner.lifecycle
        val viewBinding = viewBinder(thisRef)
        if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
            Log.w(
                    "ViewBindingProperty", "Access to viewBinding after Lifecycle is destroyed or hasn't created yet. " +
                    "The instance of viewBinding will be not cached."
            )
            // We can access to ViewBinding after Fragment.onDestroyView(), but don't save it to prevent memory leak
        } else {
            lifecycle.addObserver(ClearOnDestroyLifecycleObserver())
            this.viewBinding = viewBinding
        }
        return viewBinding
    }

    @MainThread
    fun clear() {
        viewBinding = null
    }

    private inner class ClearOnDestroyLifecycleObserver : LifecycleObserver {

        private val mainHandler = Handler(Looper.getMainLooper())

        @MainThread
        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        fun onDestroy(owner: LifecycleOwner) {
            owner.lifecycle.removeObserver(this)
            mainHandler.post { clear() }
        }
    }
}

inline fun <reified VB : ViewBinding> Activity.inflate() = lazy {
    inflateBinding<VB>(layoutInflater).apply { setContentView(root) }
}

inline fun <reified VB : ViewBinding> Dialog.inflate() = lazy {
    inflateBinding<VB>(layoutInflater).apply { setContentView(root) }
}

@Suppress("UNCHECKED_CAST")
inline fun <reified VB : ViewBinding> inflateBinding(layoutInflater: LayoutInflater) =
        VB::class.java.getMethod("inflate", LayoutInflater::class.java)
                .invoke(null, layoutInflater) as VB


fun <T : ViewBinding> Activity.inflate(inflater: (LayoutInflater) -> T) = lazy {
    inflater(layoutInflater).apply { setContentView(root) }
}

fun <T : ViewBinding> Fragment.inflate(inflater: (LayoutInflater) -> T) = lazy {
    inflater(layoutInflater)
}

// 反射获取ViewBinding
fun <VB : ViewBinding> Activity.viewBindings(inflater: LayoutInflater): VB {
    val vbClass =
            (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.filterIsInstance<Class<VB>>()
    val inflate = vbClass[0].getDeclaredMethod("inflate", LayoutInflater::class.java)
    return inflate.invoke(null, inflater) as VB
}

// 供Fragment使用
//fun <VB : ViewBinding> Any.viewBindings(inflater: LayoutInflater, container: ViewGroup?): VB {
//    val vbClass =
//        (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments.filterIsInstance<Class<VB>>()
//    val inflate = vbClass[0].getDeclaredMethod(
//        "inflate",
//        LayoutInflater::class.java,
//        ViewGroup::class.java,
//        Boolean::class.java
//    )
//    return inflate.invoke(null, inflater, container, false) as VB
//}
